package com.fy.market.persistent.respository.strategy;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.json.JSONUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.fy.market.persistent.mapper.*;
import com.fy.market.persistent.po.*;
import com.fy.market.persistent.redis.IRedisService;
import com.fy.market.strategy.model.dto.UserQueryDTO;
import com.fy.market.strategy.model.entity.*;
import com.fy.market.strategy.model.entity.tree.RuleTreeEntity;
import com.fy.market.strategy.model.entity.tree.RuleTreeNodeEntity;
import com.fy.market.strategy.model.entity.tree.RuleTreeNodeLineEntity;
import com.fy.market.strategy.model.entity.tree.valobj.RuleLimitTypeVO;
import com.fy.market.strategy.model.entity.tree.valobj.RuleTreeNodeLineVO;
import com.fy.market.strategy.model.entity.tree.valobj.RuleTreeNodeVO;
import com.fy.market.strategy.model.entity.tree.valobj.RuleTreeVO;
import com.fy.market.strategy.model.enums.StatusEnum;
import com.fy.market.strategy.repository.IStrategyRepository;
import com.fy.market.types.common.constant.KeysConstant;
import com.fy.market.types.common.ex.ExHandler;
import lombok.Builder;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.core.io.ClassPathResource;
import org.springframework.stereotype.Repository;

import java.nio.charset.StandardCharsets;
import java.util.List;
import java.util.stream.IntStream;

import javax.annotation.Resource;
import java.util.*;
import java.util.stream.Collectors;

import static org.springframework.beans.BeanUtils.copyProperties;

/**
 * 策略仓储实现
 *
 * @author fwq
 * @date 2025-08-11 10:00
 * @desc 策略仓储实现
 */
@Slf4j
@Repository
public class StrategyRepository extends ServiceImpl<StrategyMapper, Strategy> implements IStrategyRepository<Strategy> {
    @Resource
    private UserMapper userMapper;
    @Resource
    private AwardMapper awardMapper;
    @Resource
    private IRedisService iRedisService;
    @Resource
    private RabbitTemplate rabbitTemplate;
    @Resource
    private RuleTreeMapper ruleTreeMapper;
    @Resource
    private RuleModelMapper ruleModelMapper;
    @Resource
    private RuleTreeNodeMapper ruleTreeNodeMapper;
    @Resource
    private StrategyAwardMapper strategyAwardMapper;
    @Resource
    private RuleTreeNodeLineMapper ruleTreeNodeLineMapper;
    @Resource
    private StrategyAwardRuleMapper strategyAwardRuleMapper;
    @Resource
    private StrategyRuleWeightMapper strategyRuleWeightMapper;
    @Resource
    private StrategyRuleBlacklistMapper strategyRuleBlacklistMapper;


    /**
     * 获取策略实体
     *
     * @param strategyId
     * @return 策略实体
     */
    @Override
    public StrategyEntity getStrategyEntity(Integer strategyId) {
        StrategyEntity strategyEntity = iRedisService.getValue(KeysConstant.RedisKey.STRATEGY_KEY + strategyId);
        if (strategyEntity != null) return strategyEntity;

        Strategy strategy = getOne(new LambdaQueryWrapper<Strategy>()
                .eq(Strategy::getStrategyId, strategyId)
                .eq(Strategy::getStatus, StatusEnum.ENABLE.getCode()));
        if (strategy == null) return new StrategyEntity();

        strategyEntity = new StrategyEntity();
        copyProperties(strategy, strategyEntity);
        List<Integer> ruleModelIdList = strategyEntity.getStrategyRuleModelIdList();
        List<RuleModelEntity> ruleModelEntities = IntStream.range(0, ruleModelIdList.size())
                .mapToObj(i -> {
                    Integer ruleModelId = ruleModelIdList.get(i);
                    RuleModel ruleModel = ruleModelMapper.selectById(ruleModelId);
                    RuleModelEntity ruleModelEntity = new RuleModelEntity();
                    copyProperties(ruleModel, ruleModelEntity);
                    ruleModelEntity.setPriority(i + 1); // 按照策略中的顺序设置优先级
                    return ruleModelEntity;
                })
                .collect(Collectors.toList());

        strategyEntity.setStrategyRuleModels(ruleModelEntities);
        iRedisService.setValue(KeysConstant.RedisKey.STRATEGY_KEY + strategyId, strategyEntity);
        return strategyEntity;
    }

    /**
     * 查询权重总和
     *
     * @param strategyId
     * @return
     */
    @Override
    public Integer queryAllRate(Integer strategyId, Integer matchedTimes) {
        return iRedisService.getValue(strategyId + ":" + KeysConstant.RedisKey.STRATEGY_RULE_WEIGHT_COUNT + matchedTimes);
    }

    /**
     * 添加默认奖品到缓存
     *
     * @param strategyId             策略ID
     * @param rangeIndexToAwardIdMap 权重范围到奖品ID的映射
     */
    @Override
    public void addDefaultRedisCache(Integer strategyId, Map<Integer, Integer> rangeIndexToAwardIdMap) {
        // 2. 存储概率查找表
        Map<Integer, Integer> cacheRateTable = iRedisService.getMap(strategyId + ":" + KeysConstant.RedisKey.STRATEGY_DEFAULT_TOTAL);
        cacheRateTable.putAll(rangeIndexToAwardIdMap);

        iRedisService.setValue(strategyId + ":" + KeysConstant.RedisKey.STRATEGY_DEFAULT_COUNT, cacheRateTable.size());
    }


    /**
     * 缓存策略奖品库存
     *
     * @param strategyId 策略ID
     */
    @Override
    public void addStockCache(Integer strategyId) {
        List<StrategyAwardRuleEntity> strategyAwardRuleEntities = this.queryStrategyAwardRuleEntities(strategyId);
        // 存储库存查找表
        strategyAwardRuleEntities.forEach(strategyAwardRuleEntity -> {
            Integer awardId = strategyAwardRuleEntity.getAwardId();
            Integer awardCountSurplus = strategyAwardRuleEntity.getAwardCountSurplus();

            String stockKey = KeysConstant.RedisKey.STRATEGY_AWARD_STOCK + strategyId;
            iRedisService.addToMap(stockKey, awardId, awardCountSurplus);
        });
    }

    /**
     * 添加策略规则权重奖品到Redis缓存
     *
     * @param strategyId             策略ID
     * @param rangeIndexToAwardIdMap 权重分组映射，键为权重值，值为对应的奖品ID列表
     */
    @Override
    public void addRuleWeightRedisCache(Integer strategyId, Integer totalRate, Integer times, Map<Integer, Map<Integer, Integer>> rangeIndexToAwardIdMap) {

        // 2. 存储概率查找表
        Map<Integer, Integer> cacheRateTable = iRedisService.getMap(strategyId + ":" + KeysConstant.RedisKey.STRATEGY_RULE_WEIGHT + times);
        cacheRateTable.putAll(rangeIndexToAwardIdMap.get(times));

        iRedisService.setValue(strategyId + ":" + KeysConstant.RedisKey.STRATEGY_RULE_WEIGHT_COUNT + times, totalRate);
    }

    /**
     * 添加所有权重总和到Redis缓存
     *
     * @param strategyId 策略ID
     * @param allRate    所有权重总和
     */
    @Override
    public void addAllRateCache(Integer strategyId, Integer allRate) {
        iRedisService.setValue(strategyId + ":" + KeysConstant.RedisKey.STRATEGY_RULE_WEIGHT_TOTAL, allRate);
    }

    /**
     * 执行抽奖
     *
     * @param strategyId
     * @param randomIndex
     * @return 奖品ID
     */
    @Override
    public Integer doRaffle(Integer strategyId, Integer matchedTimes, Integer randomIndex) {
        return iRedisService.getFromMap(strategyId + ":" + KeysConstant.RedisKey.STRATEGY_RULE_WEIGHT + matchedTimes, randomIndex);
    }

    /**
     * 获取规则模型列表
     *
     * @param strategyId 策略ID
     * @return 规则模型列表
     */
    @Override
    public List<StrategyRuleModelEntity> getRuleModelList(Integer strategyId) {
        List<StrategyRuleModelEntity> strategyRuleModelEntityList;
        strategyRuleModelEntityList = iRedisService.getValue(KeysConstant.RedisKey.STRATEGY_RULE_MODEL + strategyId);
        if (CollectionUtils.isNotEmpty(strategyRuleModelEntityList)) return strategyRuleModelEntityList;

        Strategy strategy = getOne(new LambdaQueryWrapper<Strategy>()
                .eq(Strategy::getStrategyId, strategyId)
                .eq(Strategy::getStatus, StatusEnum.ENABLE.getCode()));
        if (strategy == null) return strategyRuleModelEntityList;

        StrategyEntity strategyEntity = new StrategyEntity();
        copyProperties(strategy, strategyEntity);
        List<Integer> ruleModelIdList = strategyEntity.getStrategyRuleModelIdList();
        if (CollectionUtils.isEmpty(ruleModelIdList)) return strategyRuleModelEntityList;

        strategyRuleModelEntityList = ruleModelIdList.stream()
                .map(ruleModelId -> {
                    RuleModel ruleModel = ruleModelMapper.selectById(ruleModelId);
                    if (ruleModel == null) return null;
                    StrategyRuleModelEntity strategyRuleModelEntity = new StrategyRuleModelEntity();
                    copyProperties(ruleModel, strategyRuleModelEntity);
                    strategyRuleModelEntity.setRuleModelId(ruleModel.getId());
                    return strategyRuleModelEntity;
                })
                .filter(Objects::nonNull)
                .collect(Collectors.toList());

        if (CollectionUtils.isNotEmpty(strategyRuleModelEntityList))
            iRedisService.setValue(KeysConstant.RedisKey.STRATEGY_RULE_MODEL + strategyId, strategyRuleModelEntityList);
        return strategyRuleModelEntityList;
    }

    /**
     * 查询策略规则权重列表，并根据权重分组
     *
     * @param strategyId 策略ID
     * @return 权重分组映射，键为权重值，值为对应的奖品ID列表
     */
    @Override
    public Map<Integer, List<Integer>> queryRuleWeightGroupByTimes(Integer strategyId) {
        List<StrategyRuleWeight> list = strategyRuleWeightMapper.selectList(new LambdaQueryWrapper<StrategyRuleWeight>()
                .eq(StrategyRuleWeight::getStrategyId, strategyId)
                .eq(StrategyRuleWeight::getStatus, StatusEnum.ENABLE.getCode()));
        return list.stream().collect(Collectors.groupingBy(
                StrategyRuleWeight::getReachTimesExist,
                Collectors.mapping(StrategyRuleWeight::getStrategyAwardId, Collectors.toList())
        ));
    }

    /**
     * 查询策略规则黑名单用户
     *
     * @param ruleMatterEntity 抽奖因子
     * @return 策略规则黑名单用户实体
     */
    @Override
    public StrategyRuleBlacklistEntity queryBlackList(RuleMatterEntity ruleMatterEntity) {
        StrategyRuleBlacklist strategyRuleBlacklist = strategyRuleBlacklistMapper.selectOne(new LambdaQueryWrapper<StrategyRuleBlacklist>()
                .eq(StrategyRuleBlacklist::getStrategyId, ruleMatterEntity.getStrategyId())
                .eq(StrategyRuleBlacklist::getUserId, ruleMatterEntity.getUserId())
                .eq(StrategyRuleBlacklist::getStatus, StatusEnum.ENABLE.getCode()));

        StrategyRuleBlacklistEntity strategyRuleBlacklistEntity = new StrategyRuleBlacklistEntity();
        if (strategyRuleBlacklist == null) return strategyRuleBlacklistEntity;
        BeanUtil.copyProperties(strategyRuleBlacklist, strategyRuleBlacklistEntity);
        return strategyRuleBlacklistEntity;
    }

    /**
     * 查询策略规则列表
     *
     * @param strategyId 策略ID
     * @return 策略规则实体列表
     */
    @Override
    public List<StrategyRuleEntity> queryStrategyRuleList(Integer strategyId) {
        return strategyAwardRuleMapper.selectList(new LambdaQueryWrapper<StrategyAwardRule>()
                        .eq(StrategyAwardRule::getStrategyId, strategyId)
                        .eq(StrategyAwardRule::getStatus, StatusEnum.ENABLE.getCode()))
                .stream()
                .map(strategyAwardRule -> {
                    StrategyRuleEntity strategyRuleEntity = new StrategyRuleEntity();
                    if (strategyRuleEntity != null && strategyAwardRule.getRuleModelId() != null) {
                        RuleModel ruleModel = ruleModelMapper.selectById(strategyAwardRule.getRuleModelId());
                        BeanUtil.copyProperties(ruleModel, strategyRuleEntity);
                    }
                    BeanUtil.copyProperties(strategyAwardRule, strategyRuleEntity);
                    return strategyRuleEntity;
                }).collect(Collectors.toList());
    }

    /**
     * 查询策略奖品规则实体列表
     *
     * @param strategyId 策略ID
     * @return 策略奖品规则实体列表
     */
    @Override
    public List<StrategyAwardRuleEntity> queryStrategyAwardRuleEntities(Integer strategyId) {
        // 检查传入的策略 ID 是否为空
        if (strategyId == null) return List.of();

        // 构建 Redis 缓存键
        String redisStrategyAwardCacheKey = KeysConstant.RedisKey.STRATEGY_AWARD_KEY + strategyId;
        // 从 Redis 缓存中获取策略奖品实体列表
        List<StrategyAwardRuleEntity> strategyAwardRuleEntityList = iRedisService.getValue(redisStrategyAwardCacheKey);
        // 如果缓存中存在数据，则直接返回
        if (strategyAwardRuleEntityList != null) {
            List<StrategyAwardRuleEntity> strategyAwardEntityCacheList = JSONUtil.toList(strategyAwardRuleEntityList.toString(), StrategyAwardRuleEntity.class);
            log.info("从 Redis 缓存中获取策略奖品实体列表：{}", strategyAwardEntityCacheList);
            return strategyAwardEntityCacheList;
        }

        // 从数据库中查询策略奖品列表
        List<StrategyAward> strategyAwardList = strategyAwardMapper.selectList(
                new LambdaQueryWrapper<StrategyAward>()
                        .eq(StrategyAward::getStrategyId, strategyId)
                        .eq(StrategyAward::getStatus, StatusEnum.ENABLE.getCode())
        );

        if (strategyAwardList.isEmpty() || strategyAwardList.size() == 0) return List.of();
        // 将数据库查询结果转换为实体列表
        List<StrategyAwardRuleEntity> strategyAwardRuleEntities = strategyAwardList.stream()
                .map(strategyAward -> {
                    StrategyAwardRuleEntity strategyAwardRuleEntity = new StrategyAwardRuleEntity();
                    copyProperties(strategyAward, strategyAwardRuleEntity);

                    Award award = awardMapper.selectOne(new LambdaQueryWrapper<Award>()
                            .eq(Award::getId, strategyAwardRuleEntity.getAwardId())
                            .eq(Award::getStatus, StatusEnum.ENABLE.getCode())
                    );

                    strategyAwardRuleEntity.setAwardKey(award.getAwardKey());
                    strategyAwardRuleEntity.setAwardConfig(award.getAwardConfig());
                    strategyAwardRuleEntity.setAwardDesc(award.getAwardDesc());

                    strategyAwardRuleEntity.setRuleModels(strategyAwardRuleMapper.selectList(new LambdaQueryWrapper<StrategyAwardRule>()
                                    .eq(StrategyAwardRule::getStrategyId, strategyId)
                                    .eq(StrategyAwardRule::getStrategyAwardId, strategyAward.getId())
                                    .eq(StrategyAwardRule::getStatus, StatusEnum.ENABLE.getCode()))
                            .stream()
                            .map(strategyAwardRule -> {
                                RuleModel ruleModel = ruleModelMapper.selectOne(new LambdaQueryWrapper<RuleModel>()
                                        .eq(RuleModel::getId, strategyAwardRule.getRuleModelId())
                                        .eq(RuleModel::getStatus, StatusEnum.ENABLE.getCode()));
                                RuleModelEntity ruleModelEntity = new RuleModelEntity();
                                copyProperties(ruleModel, ruleModelEntity);

                                StrategyAwardRule awardRuleRepositoryOne = strategyAwardRuleMapper.selectOne(new LambdaQueryWrapper<StrategyAwardRule>()
                                        .eq(StrategyAwardRule::getStrategyId, strategyAwardRule.getStrategyId())
                                        .eq(StrategyAwardRule::getStrategyAwardId, strategyAwardRule.getStrategyAwardId())
                                        .eq(StrategyAwardRule::getRuleModelId, strategyAwardRule.getRuleModelId())
                                        .eq(StrategyAwardRule::getStatus, StatusEnum.ENABLE.getCode()));
                                if (awardRuleRepositoryOne != null)
                                    ruleModelEntity.setAwardUnlockCount(awardRuleRepositoryOne.getAwardUnlockCount());
                                return ruleModelEntity;
                            })
                            .collect(Collectors.toList()));

                    // 修复第109-115行的代码
                    strategyAwardRuleEntity.getRuleModels()
                            .stream()
                            .filter(ruleModel -> ruleModel.getAwardUnlockCount() != null)
                            .findFirst()
                            .ifPresent(ruleModel -> {
                                strategyAwardRuleEntity.setAwardUnlockCount(ruleModel.getAwardUnlockCount());
                            });
                    return strategyAwardRuleEntity;
                })
                .collect(Collectors.toList());

        // 将查询结果存入 Redis 缓存
        if (!strategyAwardRuleEntities.isEmpty())
            iRedisService.setValue(redisStrategyAwardCacheKey, strategyAwardRuleEntities);

        return strategyAwardRuleEntities;
    }


    /**
     * 根据树id组装获取决策树
     *
     * @param ruleMatterEntity
     * @return 决策树
     */
    @Override
    public RuleTreeVO getRuleTreeVO(RuleMatterEntity ruleMatterEntity) {

        String treeId = "tree_lock";

        //  TODO 以下注释部分是通过数据库或者缓存获取决策树的逻辑,目前暂时设置成所有抽奖都走决策树,默认赋值 tree_lock
//        Integer strategyId = ruleMatterEntity.getStrategyId();
//        Integer awardId = Optional.ofNullable(ruleMatterEntity.getAwardId())
//                .orElse(ruleMatterEntity.getGuaranteedAwardId());

//        // 使用Stream API构建treeId，类似："rule_lock,rule_luck_award"
//        String treeId = this.queryStrategyAwardRuleEntities(strategyId).stream()
//                .filter(entity -> entity.getAwardId().equals(awardId)
//                        && entity.getStrategyId().equals(strategyId)
//                        && !entity.getRuleModels().isEmpty())
//                .flatMap(entity -> entity.getRuleModels().stream())
//                .map(RuleModelEntity::getRuleModel)
//                .collect(Collectors.joining(","));
//
        // 优先从缓存获取
        String cacheKey = KeysConstant.RedisKey.RULE_TREE_VO_KEY + treeId;
        RuleTreeVO ruleTreeVOCache = iRedisService.getValue(cacheKey);
        if (null != ruleTreeVOCache) return ruleTreeVOCache;

        // 从数据库获取
        RuleTree ruleTree = ruleTreeMapper.selectOne(new LambdaQueryWrapper<RuleTree>().eq(RuleTree::getTreeId, treeId));
        List<RuleTreeNode> ruleTreeNodes = ruleTreeNodeMapper.selectList(new LambdaQueryWrapper<RuleTreeNode>().eq(RuleTreeNode::getTreeId, treeId));
        List<RuleTreeNodeLine> ruleTreeNodeLines = ruleTreeNodeLineMapper.selectList(new LambdaQueryWrapper<RuleTreeNodeLine>().eq(RuleTreeNodeLine::getTreeId, treeId));

        ExHandler.throwIf(ruleTree == null || ruleTreeNodes == null || ruleTreeNodeLines == null, "决策树未配置！");
        // 1. 使用Stream API构建tree node line Map结构
        Map<String, List<RuleTreeNodeLineVO>> ruleTreeNodeLineMap = ruleTreeNodeLines.stream()
                .collect(Collectors.groupingBy(
                        RuleTreeNodeLine::getRuleNodeFrom,
                        Collectors.mapping(line -> RuleTreeNodeLineVO.builder()
                                .treeId(line.getTreeId())
                                .ruleNodeFrom(line.getRuleNodeFrom())
                                .ruleNodeTo(line.getRuleNodeTo())
                                .ruleLimitType(RuleLimitTypeVO.valueOf(line.getRuleLimitType()))
                                .ruleLimitValue(line.getRuleLimitValue())
                                .build(), Collectors.toList())));

        // 2. 使用Stream API构建tree node Map结构
        Map<String, RuleTreeNodeVO> treeNodeMap = ruleTreeNodes.stream()
                .collect(Collectors.toMap(
                        RuleTreeNode::getRuleKey,
                        node -> RuleTreeNodeVO.builder()
                                .treeId(node.getTreeId())
                                .ruleKey(node.getRuleKey())
                                .ruleDesc(node.getRuleDesc())
                                .ruleValue(node.getRuleValue())
                                .treeNodeLineVOList(ruleTreeNodeLineMap.get(node.getRuleKey()))
                                .build()));

        // 3. 构建 Rule Tree
        RuleTreeVO ruleTreeVODB = RuleTreeVO.builder()
                .treeId(ruleTree.getTreeId())
                .treeName(ruleTree.getTreeName())
                .treeDesc(ruleTree.getTreeDesc())
                .treeRootRuleNode(ruleTree.getTreeRootRuleKey())
                .treeNodeMap(treeNodeMap)
                .build();

        iRedisService.setValue(cacheKey, ruleTreeVODB);
        return ruleTreeVODB;
    }

    /**
     * 扣减奖品库存
     *
     * @param strategyId 策略ID
     * @param awardId    奖品ID
     * @return 扣减结果实体
     */
    @Override
    public DecrAwardStockResultEntity decrAwardStock(Integer strategyId, Integer awardId) {

        // 1. 使用Lua脚本扣减库存
        log.info("🔄 执行Lua脚本扣减库存...");
        List<Object> result = iRedisService.deductStock(strategyId, awardId);

        DecrAwardStockResultEntity decrAwardStockResultEntity = DecrAwardStockResultEntity.builder()
                .CODE(parseInteger(result.get(0)))
                .REMAINING_STOCK(parseInteger(result.get(1)))
                .build();
        this.sendOrder(OrderMessageEntity.builder().strategyId(strategyId).awardId(awardId).userId(3).build());
        return decrAwardStockResultEntity;
    }

    /**
     * 解析Integer值
     *
     * @param obj 对象
     * @return Integer值
     */
    private Integer parseInteger(Object obj) {
        if (obj == null) return 0;
        if (obj instanceof Integer) return (Integer) obj;
        if (obj instanceof Long) return ((Long) obj).intValue();
        return Integer.parseInt(obj.toString());
    }

    /**
     * 解析String值
     *
     * @param obj 对象
     * @return 字符串值
     */
    private String parseString(Object obj) {
        if (obj == null) return "";
        return obj.toString();
    }

    /**
     * 读取Lua脚本文件
     *
     * @param scriptPath 脚本路径
     * @return 脚本内容
     */
    private String readLuaScript(String scriptPath) {
        try {
            ClassPathResource resource = new ClassPathResource(scriptPath);
            return new String(resource.getInputStream().readAllBytes(), StandardCharsets.UTF_8);
        } catch (Exception e) {
            log.error("读取Lua脚本失败:{}", scriptPath, e);
            throw new RuntimeException("读取Lua脚本失败: " + scriptPath, e);
        }
    }

    /**
     * 发送订单到消息队列
     *
     * @param orderMessageEntity 订单消息实体
     */
    @Override
    public void sendOrder(OrderMessageEntity orderMessageEntity) {
        log.info("发送订单消息到队列，策略ID:{} 奖品ID:{} 用户ID:{}", orderMessageEntity.getStrategyId(), orderMessageEntity.getAwardId(), orderMessageEntity.getUserId());
        rabbitTemplate.convertAndSend(KeysConstant.MQKey.ORDER_CONSUME_EXCHANGE, KeysConstant.MQKey.ORDER_CONSUME_ROUTING_KEY, orderMessageEntity);
    }
}
